package ba.unsa.etf.pikks.mccabe;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

import ba.unsa.etf.pikks.objekti.PredProcesiranje;



public  class McCabe {
	public McCabe() {}
	
	public HashMap<String, Integer> getMcCabe(File file) throws Exception {
		int methodStartLoc;
		int methodEndLoc;
		int cyclomaticValue_temp;
		String methodName_temp;
		
		// return variable of this method
		HashMap<String, Integer> cyclomaticValue = new HashMap<String, Integer>();
		
		// parse the code and get a list of methods into the methodMap
		PredProcesiranje fileParser = new PredProcesiranje(file);
		
		// Line Index
		int index=1;
		
		// initialize the variable 
		methodStartLoc = 1;

		//cycle by all methods present in the class
		for (int i=1;i<=fileParser.methodMap.size();i++) {
			//Identify the start line of the next method
			while (fileParser.methodMap.get(index)==null && index <= fileParser.line_number) {
			  index++;
			}  
			methodStartLoc = index;
			
			//go to last line of the method
			index++;
			while (fileParser.methodMap.get(index)==null && index <= fileParser.line_number) {
				  index++;
				}
			methodEndLoc = index-1;
			
			// get the name of the method removing unnecessary part like a bracket
			methodName_temp = fileParser.methodMap.get(methodStartLoc);
			
			// if it's a class, check the lines until the end of the file
			// if the maximum value is passed to getCyclomaticValue(), it will look at the code until the EOF
			if ( methodName_temp.indexOf(" class ") >= 0 ) {
				
				// gets the cyclomatic value of the class and put it into the HashMap
				cyclomaticValue_temp = getCyclomaticValue(file, methodStartLoc, Integer.MAX_VALUE);
				cyclomaticValue.put("Class", cyclomaticValue_temp);
			} else {
				
				// gets the cyclomatic value of the method and put it into the HashMap
				cyclomaticValue_temp = getCyclomaticValue(file, methodStartLoc, methodEndLoc);
				
				methodName_temp = getMethodName(methodName_temp);
				cyclomaticValue.put(methodName_temp, cyclomaticValue_temp);
			}
			
			
			// to check the value easily (just for debugging)
			System.out.println(methodName_temp + ":  " + cyclomaticValue_temp);
		}		

		return cyclomaticValue;		
	}
	
	private int getCyclomaticValue (File file, int startPosition, int endPosition) throws Exception {
		int cyclomaticValue = 1;
		int lineNumber = 0;
		String codeLine;
		boolean commentInProgress = false;
		
		String[] keywordList = { "IF", "CASE", "WHILE", "FOR", "&&", "||", "CATCH" };
		
		Scanner diskScanner = new Scanner(file);
		
		do {
			
			codeLine = diskScanner.nextLine();
			lineNumber++;

			// check the lines from the start point of the file, which means the method starts
			if ( lineNumber >= startPosition && lineNumber < endPosition) { 
				
				// if there is a comment in the line, remove it
				if ( codeLine.indexOf("//") >= 0 ) {
					String[] lines_temp = codeLine.split("//");
					if (lines_temp.length > 0) {
						codeLine = lines_temp[0];
					}
				}
				
				// if there is a comment in the line, remove it
				if ( codeLine.indexOf("/*") >= 0 ) {
					String[] lines_temp = codeLine.split("/*");
					if (lines_temp.length > 0) codeLine = lines_temp[0];
					commentInProgress = true;
					
					if( lines_temp[1].indexOf("*/") >= 0 ) commentInProgress = false;
				}						
				
				if ( codeLine.indexOf("*/") >= 0 ) {
					String[] lines_temp = codeLine.split("/*");
					codeLine = lines_temp[lines_temp.length - 1];
					commentInProgress = false;
				}
				
				if(!isComment(codeLine) && !commentInProgress)
				{
					int startQuote = -1;
					ArrayList<Integer> quoteChars = new ArrayList<Integer>();
					
					for(int i = 0; i < codeLine.length(); i++)
					{
						if((codeLine.indexOf("\"", i) == i) && (codeLine.indexOf("\\") == i-1) && (startQuote == -1))
						{
							startQuote = i;
						}
						else if((codeLine.indexOf("\"", startQuote) == i)&& (codeLine.indexOf("\\", startQuote) == i-1) && (startQuote != -1))
						{
							quoteChars.add((Integer)startQuote);
							startQuote = -1;
							quoteChars.add((Integer)i);
						}
						
					}
					
					StringBuffer temp = new StringBuffer();
					int index = 0;
					
					for(int i = 0; i < quoteChars.size(); i = i + 2)
					{
						int first = quoteChars.get(i);
						int second = quoteChars.get(i+1);
						for(int j = index; (j < codeLine.length() && j <= second); j++)
						{
							if(j < first)
							{
								temp.append(codeLine.charAt(j));
							}
							index++;
						}
					}
						
				}
				
				// if the line is not a comment line, check inside to calculate the cyclomatic complexity
				if ( !isComment(codeLine) && !commentInProgress)
					for (int i = 0; i < keywordList.length; i++) {
						if ( (codeLine.toUpperCase()).indexOf(keywordList[i].toUpperCase()) >= 0) {
							cyclomaticValue++;
							String[] lines_temp = (codeLine.toUpperCase()).split(keywordList[i].toUpperCase());
							
							if ( lines_temp.length > 1 ) {
								codeLine = lines_temp[1];
							}
						}
					}
					
				}
		} while (diskScanner.hasNext());
		
		return cyclomaticValue;
	}
	
	
	private boolean isComment(String line) {
		if ( line.indexOf("//") >= 0 || line.indexOf("/*") >= 0) {
			return true;
		}
		
		return false;
	}
	
	
	private String getMethodName(String line) {
		int end = line.indexOf(")");
		int begin = line.indexOf("p");
		String methodName = line.substring(begin, end + 1);
		return methodName;
	}
}